#include "png8cropfx.h"
#include "mnu_parser.h"

namespace png8crop
{

	mnu_parser::mnu_parser(void)
		: constants(),
		variables(),
		typedefs(),
		current(NULL)
	{
		this->register_type(vt_dword, sizeof(int));
		this->register_type(vt_word, sizeof(short));
		this->register_type(vt_byte, sizeof(char));
	}

	mnu_parser::~mnu_parser(void)
	{
	}

	void mnu_parser::register_const(const _TCHAR * indentifier, const void * value, value_type type)
	{
		this->constants[indentifier] = const_info(indentifier, value, type);
	}


	void mnu_parser::register_type(value_type type, size_t unit)
	{
		this->typedefs[type] = unit;
	}


	void mnu_parser::register_variable(const _TCHAR * indentifier, void * ptr, value_type type, 
		diam_list* diams, variable_callback callback, void * cbparam)
	{
		this->variables[indentifier] = 
			variable_info(indentifier, ptr, type, diams, callback, cbparam);
	}


	void mnu_parser::parse_line(const _TCHAR* line) throw(ex_internal)
	{ 
		this->current = line;

		this->current = this->skip_spaces();

		if(*this->current == _T('\0'))
		{
			return;
		}
		else if(*this->current == _T('#'))
		{
			this->parse_remarks();
		}
		else if(isalpha(*this->current))
		{
			const _TCHAR * word_end = this->skip_idnchar();

			_tstring str_command(current, word_end);

			if(str_command == _T("set"))
			{
				this->current = word_end;
				this->parse_set();
			}
			else
			{
				throw ex_internal(_T("Not recognized command"), str_command.c_str());
			}
		}
		else
		{
			throw ex_internal(_T("Unexpected characters"), this->current);
		}
	}

	void mnu_parser::parse_set()
	{
		this->current = this->skip_spaces();

		const _TCHAR * word_end = this->skip_idnchar();

		_tstring variable_idn(current, word_end);
		variable_info &varinfo = this->variables[variable_idn];

		byte* varaddr = (byte *) varinfo.value;
		//const void* value = NULL;

		if(varinfo.key == NULL)
		{
			throw ex_internal(_T("Not registered variable"), variable_idn.c_str());
		}

		this->current = word_end;

		if(varinfo.diamlist != NULL && varinfo.diamlist->diam_count > 0)
		{
			this->current = this->skip_spaces();
			if(*this->current == _T('('))
			{
				int max_diam = varinfo.diamlist->diam_count;

				int diam = 0;
				for( ; diam < max_diam; ++diam)
				{
					int diam_val;
					this->current++;

					this->parse_const(&diam_val, vt_dword);

					if(diam_val <= 0 || diam_val > varinfo.diamlist->diams[diam].scales)
					{
						throw ex_internal(_T("Index of array is out of range"), varinfo.key);
					}

					varaddr += varinfo.diamlist->diams[diam].bytes * (diam_val - 1);

					this->current = this->skip_spaces();

					if(*this->current == _T(','))
					{
						continue;
					}
					else if(*this->current == _T(')'))
					{
						break;
					}
					else
					{
						throw ex_internal(_T("Unexpected characters"), this->current);
					}
				}

				diam++;
				if(diam == max_diam)
				{
					this->current++;
				}
				else
				{
					throw ex_internal(_T("Count of indexer beyond array's diamensions"), this->current);
				}

			}
		}

		this->parse_const(varaddr, varinfo.type);

		if(varinfo.callback != NULL)
		{
			varinfo.callback(varaddr, varinfo.cbparam);
		}

		return;
	}


	void mnu_parser::parse_remarks()
	{
		return;
	}

	void mnu_parser::parse_const(void* addr, value_type expected_type)
	{
		const _TCHAR * word_end;
		this->current = this->skip_spaces();

		word_end = this->skip_idnchar();

		_tstring word(this->current, word_end);
		size_t cb = this->typedefs[expected_type];

		if(cb == 0)
		{
			throw ex_internal(_T("Type is not defined."));
		}

		int digital_value;
		value_type type = vt_na;
		const void* value_src;

		if(this->is_idnstart(word.at(0)))
		{
			//indentifier const
			const_info &info = this->constants[word];
			if(info.key != NULL)
			{
				type = info.type;
				value_src = info.value;
			}
			else
			{
				throw ex_internal(_T("Not registered identifier."), word.c_str());
			}
		}
		else if(isdigit(word.at(0)))
		{
			size_t parse_end;

			type = expected_type;
			value_src = &digital_value;

			digital_value = stoi(word, &parse_end);

			if(word.length() != parse_end)
			{
				throw ex_internal(_T("Unexpected characters"), word.c_str());
			}
		}
		else
		{
			throw ex_internal(_T("Unexpected characters"), word.c_str());
		}

		if(type != expected_type)
		{
			throw ex_internal(_T("Unexpected value type"), word.c_str());
		}

		memcpy(addr, value_src, cb);

		this->current = word_end;
	}
}